package server1

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"zinxtcp/message"
	"zinxtcp/tunny"
	"zinxtcp/utils"
	"zinxtcp/ziface"
)

// Error type
var (
	ErrConnClosing   = errors.New("use of closed network connection")
	ErrWriteBlocking = errors.New("write packet was blocking")
	ErrReadBlocking  = errors.New("read packet was blocking")
)

type ConnWrapper struct {
	ConnID       uint32                 //当前连接的ID 也可以称作为SessionID，ID全局唯一
	Conn         *net.TCPConn           //当前连接的socket TCP套接字
	closeOnce    sync.Once              // close the conn, once, per instance
	closeFlag    int32                  // close flag
	closeChan    chan struct{}          // close chanel
	msgBuffChan  chan []byte            //有关冲管道，用于读、写两个goroutine之间的消息通信
	property     map[string]interface{} //链接属性
	sync.RWMutex                        //保护链接属性修改的锁
	ConnManager  ziface.IConnManager    //连接管理器
	exitChan     chan struct{}          //管理器
	joinChan     chan bool              //同步读以及写协程
	exitReceived bool
}

//创建连接的方法
func NewConnWrapper(conn *net.TCPConn, connID uint32, connMgr ziface.IConnManager, ec chan struct{}) ziface.IConnection {
	c := &ConnWrapper{
		Conn:        conn,
		ConnID:      connID,
		closeChan:   make(chan struct{}),
		msgBuffChan: make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		property:    make(map[string]interface{}), //对链接属性map初始化
		ConnManager: connMgr,
		exitChan:    ec,
		joinChan:    make(chan bool, 2),
	}
	go c.waitSignal()
	return c
}

//设置链接属性
func (c *ConnWrapper) SetProperty(key string, value interface{}) {
	c.Lock()
	defer c.Unlock()

	c.property[key] = value
}

//获取链接属性
func (c *ConnWrapper) GetProperty(key string) (interface{}, error) {
	c.RLock()
	defer c.RUnlock()

	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}

//移除链接属性
func (c *ConnWrapper) RemoveProperty(key string) {
	c.Lock()
	defer c.Unlock()

	delete(c.property, key)
}

// GetRawConn returns the raw net.TCPConn from the Conn
func (c *ConnWrapper) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

//获取当前连接ID
func (c *ConnWrapper) GetConnID() uint32 {
	return c.ConnID
}

//获取远程客户端地址信息
func (c *ConnWrapper) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// IsClosed indicates whether or not the connection is closed
func (c *ConnWrapper) IsClosed() bool {
	return atomic.LoadInt32(&c.closeFlag) == 1
}

func (c *ConnWrapper) waitSignal() {
	select {
	case <-c.exitChan:
		c.exitReceived = true
		fmt.Println(c.RemoteAddr().String(), "Conn Wrapper wait signal() get exit signal from ctx manager.")
	case <-c.closeChan:
		fmt.Println(c.RemoteAddr().String(), "Conn Wrapper wait signal() get exit signal from self close chan.")
	}
	c.Stop()
}

func (c *ConnWrapper) Join() {
	<-c.joinChan
	<-c.joinChan
}

// Close closes the connection
func (c *ConnWrapper) Stop() {
	c.closeOnce.Do(func() {
		atomic.StoreInt32(&c.closeFlag, 1)
		close(c.closeChan)
		close(c.msgBuffChan)
		connStopFunc := c.ConnManager.GetConnEvent().GetConnStop()
		connStopFunc(c)
		if c.exitReceived != true {
			go c.ConnManager.RemoveById(c.ConnID)
		}
		c.Conn.Close()
	})
}

/* 处理conn读数据的Goroutine */
func (c *ConnWrapper) StartReader() {
	fmt.Println(c.RemoteAddr().String(), "Conn Wrapper Reader Goroutine is running")
	defer func() {
		recover()
		c.Stop()
		fmt.Println(c.RemoteAddr().String(), "Conn Wrapper Reader Goroutine exit!")
		c.joinChan <- true
	}()
	//得到解码器
	unpackFunc := c.ConnManager.GetEncoder().GetUnpackFunc()

	for {
		select {
		case <-c.closeChan:
			fmt.Println(c.RemoteAddr().String(), "Reader Goroutine get signal from ConnWrapper closeChan ")
			return
		default:
			//使用解码器从tcp链接中读取对应的数据包
			msg, err := unpackFunc(c.GetTCPConnection())
			if err != nil {
				fmt.Println(c.RemoteAddr().String(), "Reader Goroutine unpackFunc error: ", err.Error())
				return
			}
			sockPack := message.NewSockPack(c, msg)
			tunny.LabourPool.Process(sockPack)
		}
	}
}

/*
   写消息Goroutine， 用户将数据发送给客户端
*/
func (c *ConnWrapper) StartWriter() {
	fmt.Println(c.RemoteAddr().String(), "Conn Wrapper Writer Goroutine is running")

	defer func() {
		recover()
		c.Stop()
		fmt.Println(c.RemoteAddr().String(), "Conn Wrapper Writer Goroutine exit!")
		c.joinChan <- true
	}()

	for {
		select {
		case <-c.closeChan:
			fmt.Println(c.RemoteAddr().String(), "Writer Goroutine get signal from ConnWrapper closeChan.")
			return
		//针对有缓冲channel需要些的数据处理
		case data := <-c.msgBuffChan:
			if c.IsClosed() {
				fmt.Println(c.RemoteAddr().String(), "msgBuffChan is Closed")
				return
			}
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println(c.RemoteAddr().String(), "Writer Goroutine Send Buff Data error:, ", err, " Conn Writer exit")
				return
			}
		}
	}
}

//启动连接，让当前连接开始工作
func (c *ConnWrapper) Start() {
	//2 开启用于写回客户端数据流程的Goroutine
	go c.StartWriter()

	//触发连接开始事件
	connBeginFunc := c.ConnManager.GetConnEvent().GetConnBegin()
	connBeginFunc(c)

	//1 开启处理该链接读取到客户端数据之后的请求业务
	go c.StartReader()
}

//将包头和负载打包后发送数据给远程的TCP客户端
func (c *ConnWrapper) SendMsg(header ziface.IHeader, data []byte) (err error) {
	if c.IsClosed() {
		return ErrConnClosing
	}
	//得到解码器
	packFunc := c.ConnManager.GetEncoder().GetPackFunc()
	dataBytes, err := packFunc(header, data)
	if err != nil {
		fmt.Println("Pack error msg type = ", header.GetMsgType())
		return errors.New("Pack error msg ")
	}
	return c.SendData(dataBytes)
}

//直接将二进制数据发送数据给远程的TCP客户端
func (c *ConnWrapper) SendData(data []byte) (err error) {
	if c.IsClosed() {
		return ErrConnClosing
	}

	defer func() {
		if e := recover(); e != nil {
			err = ErrConnClosing
		}
	}()
	select {
	case c.msgBuffChan <- data:
		return nil
	default:
		return ErrWriteBlocking
	}
}
